package cn.ecpay.unionpay.settle.controller;

import cn.ecpay.unionpay.settle.Common.DateUtil;
import cn.ecpay.unionpay.settle.Common.RespCode;
import cn.ecpay.unionpay.settle.Common.UnionpayDict;
import cn.ecpay.unionpay.settle.dao.SingleFundSettlementDao;
import cn.ecpay.unionpay.settle.entity.db.TPvFundSettleSingle;
import cn.ecpay.unionpay.settle.entity.db.TPvReturnMoney;
import cn.ecpay.unionpay.settle.util.CommonBase;
import cn.ecpay.unionpay.settle.util.FsasService;
import cn.ecpay.unionpay.settle.util.LogUtil;
import cn.ecpay.unionpay.settle.util.SDKConfig;
import org.rex.db.exception.DBException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 单笔结算
 * Created by zhaoyanzeng on 2018/11/6.
 */
@RestController
public class SingleFundSettlementController {

    @Resource
    private SingleFundSettlementDao singleFundSettlementDao;

    @RequestMapping(value = "/singleFundSettlement", method = RequestMethod.POST)
    public String singleFundSettlement(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        TPvFundSettleSingle fundSettleSingle = new TPvFundSettleSingle();
        Map<String, String> contentData = convertContentData(req);
        //该笔交易是否已经存在，如果存在的话不再发起请求直接返回重复交易
        if (isExistTxn(contentData.get("txnNo"), contentData.get("txnDate"))) {
            return "重复交易";
        }
        Map<String, String> reqData = FsasService.sign(contentData, CommonBase.encoding);            //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        reqDataConvert(fundSettleSingle, reqData);
        String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl();                            //交易请求url从配置文件读取对应属性文件fsas_sdk.properties中的 fsassdk.backTransUrl
        Map<String, String> rspData = FsasService.doPost(reqData, requestBackUrl, CommonBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过
        rspDataConvert(fundSettleSingle, rspData);
        StringBuffer parseStr = new StringBuffer("");
        if (!rspData.isEmpty()) {
            if (FsasService.validate(rspData, CommonBase.encoding)) {
                LogUtil.writeLog("验证签名成功");
                fundSettleSingle.setRespValidate("1");
                String respCode = rspData.get("respCode");
                if (RespCode.RESP_SUCCESS.equals(respCode)) {
                    //成功
                    //TODO
                } else {
                    //其他应答码为失败请排查原因
                    //TODO
                }
            } else {
                LogUtil.writeErrorLog("验证签名失败");
                fundSettleSingle.setRespValidate("0");
                //TODO 检查验证签名失败的原因
            }
        } else {
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
        }
        try {
            singleFundSettlementDao.insert(fundSettleSingle);
        } catch (DBException e) {
            LogUtil.writeErrorLog("保存结算信息失败");
            e.printStackTrace();
        }
        String reqMessage = CommonBase.genHtmlResult(reqData);
        String rspMessage = CommonBase.genHtmlResult(rspData);
//        resp.getWriter().write("商户资金结算（单笔）交易请求报文:<br/>" + reqMessage + "<br/>" + "应答报文:</br>" + rspMessage + parseStr);
        return "商户资金结算（单笔）交易请求报文:<br/>" + reqMessage + "<br/>" + "应答报文:</br>" + rspMessage + parseStr + " </br> ";
    }

    private Map<String, String> convertContentData(HttpServletRequest req) {
        String acqInsCode = req.getParameter("acqInsCode");
        String txnNo = req.getParameter("txnNo");
        String txnDate = req.getParameter("txnDate");
        String payeeAcctNo = req.getParameter("payeeAcctNo");
        String payeeAcctName = req.getParameter("payeeAcctName");
        String txnAmt = req.getParameter("txnAmt");
        String merId = req.getParameter("merId");
        String merName = req.getParameter("merName");
        //TODO 1.3 新加 01 银联 99 其他
        //TODO payeeAccType == 01 时可为空，否则必填
        String payeeAccType = req.getParameter("payeeAccType");
        String payeeBankNo = req.getParameter("payeeBankNo");
        Map<String, String> contentData = new HashMap<>();

        /***银联资金结算接入系统，产品参数，除了encoding自行选择外其他不需修改***/
        contentData.put("version", CommonBase.version);                  //版本号
        contentData.put("encoding", CommonBase.encoding);             //字符集编码 可以使用UTF-8,GBK两种方式
        contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
        contentData.put("txnType", UnionpayDict.TXN_TYPE_SINGLEFUNDSETTLEMENT); //交易类型 02-商户资金结算（单笔）
        contentData.put("currencyCode", "156");                          //币种
        contentData.put("settleMethod", "00");                           //结算方式00:T+0  01:T+N(N>0) 02:收入费用结转 03:其他
        contentData.put("txnNo", txnNo);                                    //交易流水号
        contentData.put("acqInsCode", acqInsCode);                    //机构代码
        contentData.put("txnDate", txnDate);                               //交易日期
        contentData.put("sndTime", CommonBase.getSendTime());      //发送时间 格式HHmmss
        contentData.put("merId", merId);                                    //商户号
        contentData.put("merName", merName);                           //商户名称
        contentData.put("payeeAccType", payeeAccType);              //新加 01 银联 99 其他
        contentData.put("payeeBankNo", payeeBankNo);                //收款方开户行行 号
        contentData.put("payeeAcctNo", payeeAcctNo);                //收款方账号
        contentData.put("payeeAcctName", payeeAcctName);        //收款方账户名称
        contentData.put("txnAmt", txnAmt);                                  //金额
        contentData.put("regionCd", "");                                       //地区代码 （4位数字代码）
//        contentData.put("backUrl", CommonBase.backUrl);

        return contentData;
    }


    //申请参数
    private void reqDataConvert(TPvFundSettleSingle fundSettleSingle, Map<String, String> reqData) {
        fundSettleSingle.setTxnNo(reqData.get("txnNo"));
        fundSettleSingle.setAcqInsCode(reqData.get("acqInsCode"));
        fundSettleSingle.setTxnType(reqData.get("txnType"));
        fundSettleSingle.setTxnDate(reqData.get("txnDate"));
        fundSettleSingle.setSndTime(reqData.get("sndTime"));
        fundSettleSingle.setMerId(reqData.get("merId"));
        fundSettleSingle.setMerName(reqData.get("merName"));
        fundSettleSingle.setPayeeBankNo(reqData.get("payeeBankNo"));
        fundSettleSingle.setPayeeAcctNo(reqData.get("payeeAcctNo"));
        fundSettleSingle.setPayeeAccType(reqData.get("payeeAccType"));
        fundSettleSingle.setPayeeAcctName(reqData.get("payeeAcctName"));
        fundSettleSingle.setPayerAcctNo(reqData.get("payerAcctNo"));
        fundSettleSingle.setPayerAcctName(reqData.get("payerAcctName"));
        fundSettleSingle.setCurrencyCode(reqData.get("currencyCode"));
        fundSettleSingle.setTxnAmt(reqData.get("txnAmt"));
        fundSettleSingle.setRemark(reqData.get("remark"));
        fundSettleSingle.setSettleMethod(reqData.get("settleMethod"));
        fundSettleSingle.setCreateTime(DateUtil.getDateTo19(new Date()));
    }

    //同步返回结果
    private void rspDataConvert(TPvFundSettleSingle fundSettleSingle, Map<String, String> rspData) {
        fundSettleSingle.setRespCode(rspData.get("respCode"));
        fundSettleSingle.setRespMsg(rspData.get("respMsg"));
    }

    /**
     * 交易是否已存在
     *
     * @return
     */
    public boolean isExistTxn(String txnNo, String txnDate) {
        try {
            List<TPvFundSettleSingle> list = singleFundSettlementDao.findByTxnNo(txnNo, txnDate);
            return (list != null && list.size() > 0);
        } catch (DBException e) {
            e.printStackTrace();
            return false;
        }
    }
}
